/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package agency.tango.materialintroscreen.widgets;

import java.lang.ref.WeakReference;

import ohos.agp.animation.Animator;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.*;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.utils.Color;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;

import org.jetbrains.annotations.NotNull;

import agency.tango.materialintroscreen.animations.MyValueAnimator;
import agency.tango.materialintroscreen.ResourceTable;

/**
 * @ProjectName: material-intro-screen-master
 * @Package: agency.tango.materialintroscreen.widgets
 * @ClassName: BottomSnackBar
 * @Description: 仿snackbar 底部弹出控件
 * @CreateDate: 2021/4/12 18:13
 * @UpdateDate: 2021/4/12 18:13
 * @UpdateRemark: 暂无
 * @Version: 1.0
 */
public class BottomSnackBar extends DirectionalLayout {
    private static final long DEFAULT_DURATION = 3000; // 默认BottomSnackBar停留时间
    private static final int ANIMATION_DEFAULT_DURATION = 100; // 默认BottomSnackBar弹出/收缩动画时间
    private static int DEFAULT_TRANS_Y = 2080; // 默认BottomSnackBar距离顶部偏移量
    private static int BOTTOM_SNACKBAR_HEIGHT = 120; // 默认BottomSnackBar高度
    private WeakReference<Context> mContext; // 默认BottomSnackBar高度
    private Builder builder;
    private Component defaultSnackBar; // 默认BottomSnackBar控件
    private Text mTvSnackBar; // 默认BottomSnackBar显示文字控件
    private ComponentContainer mRootView; // 当前显示BottomSnackBar页面的最顶层控件
    private EventHandler handler;
    private boolean isShow = false;
    private int defaultTransY = DEFAULT_TRANS_Y;

    private BottomSnackBar(Context context, @NotNull Builder builder) {
        super(context);
        this.mContext = new WeakReference<>(context);
        this.builder = builder;
    }

    public boolean isShow() {
        return isShow;
    }

    /**
     * 获取 Builder
     *
     * @return Builder
     */
    public Builder getBuilder() {
        if (builder == null && mContext != null && mContext.get() != null) {
            builder = new Builder(mContext.get());
        }
        return builder;
    }

    /**
     * 初始化控件
     */
    private void initWidget() {
        if (mContext == null || mContext.get() == null) {
            return;
        }
        initHandler();
        setDefaultTransY();
        initDefaultSnackBar();
        if (defaultSnackBar == null) {
            return;
        }
        BOTTOM_SNACKBAR_HEIGHT = getWidthHeightBeforeShow(defaultSnackBar)[1];
        LayoutConfig config = new LayoutConfig(LayoutConfig.MATCH_PARENT, LayoutConfig.MATCH_CONTENT);
        removeAllComponents();
        addComponent(defaultSnackBar, config);
        mContext.get().getUITaskDispatcher().asyncDispatch(this::invalidate);
    }

    /**
     * 初始化Handler
     */
    private void initHandler() {
        EventRunner eventRunner = EventRunner.getMainEventRunner();
        handler = new EventHandler(eventRunner);
    }

    /**
     * 设置默认的位移距离 保证bottomSnackBar初始位置在屏幕下方
     */
    private void setDefaultTransY() {
        if (DisplayManager.getInstance().getDefaultDisplay(mContext.get()) == null) {
            return;
        }
        if (DisplayManager.getInstance().getDefaultDisplay(mContext.get()).get() == null) {
            return;
        }
        if (DisplayManager.getInstance().getDefaultDisplay(mContext.get()).get().getAttributes() == null) {
            return;
        }
        defaultTransY = DisplayManager.getInstance().getDefaultDisplay(mContext.get()).get().getAttributes().height;
        if (defaultTransY < 0) {
            defaultTransY = 0;
        }
    }

    /**
     * 显示bottomSnackBar
     *
     * @param clickView 显示bottomSnackBar时需要点击的控件
     */
    public void show(Component clickView) {
        if (mContext == null || mContext.get() == null || clickView == null) {
            return;
        }
        if (builder == null || !builder.isNeedShowBar || isShow) {
            return;
        }
        if (mTvSnackBar != null) {
            mTvSnackBar.setText(builder.snackBarContent);
        }
        getRootView(clickView);
        if (mRootView == null) {
            return;
        }
        if (this.getComponentParent() != null) {
            mRootView.removeComponent(this);
        }
        ComponentContainer.LayoutConfig config1 = new ComponentContainer.LayoutConfig
                (ComponentContainer.LayoutConfig.MATCH_PARENT,
                        ComponentContainer.LayoutConfig.MATCH_CONTENT);
        mRootView.addComponent(this, config1);
        defaultTransY = mRootView.getHeight() > 0 ? mRootView.getHeight() : DEFAULT_TRANS_Y;
        this.setTranslationY(defaultTransY);
        beginAnimation(clickView);
    }

    private void beginAnimation(Component clickView) {
        MyValueAnimator property = MyValueAnimator.ofFloat(0, -BOTTOM_SNACKBAR_HEIGHT);
        property.setCurveType(Animator.CurveType.ACCELERATE);
        property.setDuration(builder.animationDuration);
        property.setValueUpdateListener((animatorValue, value) -> {
            float v1 = Math.abs(value / BOTTOM_SNACKBAR_HEIGHT);
            BottomSnackBar.this.setAlpha(v1);
            float v2 = defaultTransY + value;
            BottomSnackBar.this.setTranslationY((int) v2);
            clickView.setTranslationY((int) value);
        });
        addAnimaListener(property, false, clickView);
        property.start();
    }

    /**
     * 动画结束释放资源
     *
     * @param animator bottomSnackBar显示和隐藏的时候加载的动画
     */
    private void releaseResourse(Animator animator) {
        if (animator != null) {
            animator.stop();
        }
        if (mRootView != null && BottomSnackBar.this.getComponentParent() != null) {
            mRootView.removeComponent(BottomSnackBar.this);
        }
        isShow = false;
    }

    /**
     * 隐藏bottomSnackBar
     *
     * @param clickView 显示bottomSnackBar时需要点击的控件 隐藏的时候需要将该控件重新移到屏幕下方
     */
    private void hide(Component clickView) {
        MyValueAnimator propertyReverse = MyValueAnimator.ofFloat(-BOTTOM_SNACKBAR_HEIGHT, 0);
        propertyReverse.setCurveType(Animator.CurveType.DECELERATE);
        propertyReverse.setDuration(builder.animationDuration);
        propertyReverse.setValueUpdateListener((animatorValue, value) -> {
            float v1 = 1 - Math.abs(value / BOTTOM_SNACKBAR_HEIGHT);
            BottomSnackBar.this.setAlpha(v1);
            float v2 = defaultTransY + value;
            BottomSnackBar.this.setTranslationY((int) v2);
            clickView.setTranslationY((int) value);
        });
        addAnimaListener(propertyReverse, true, clickView);
        propertyReverse.start();
    }

    private void addAnimaListener(MyValueAnimator propertyReverse, boolean isReverse, Component clickView) {
        propertyReverse.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {
                if (!isReverse) {
                    isShow = true;
                }
            }

            @Override
            public void onStop(Animator animator) {
            }

            @Override
            public void onCancel(Animator animator) {
                releaseResourse(animator);
            }

            @Override
            public void onEnd(Animator animator) {
                if (isReverse) {
                    releaseResourse(animator);
                } else {
                    doAfterAnimaEnd(animator, clickView);
                }
            }

            @Override
            public void onPause(Animator animator) {
            }

            @Override
            public void onResume(Animator animator) {
            }
        });
    }

    private void doAfterAnimaEnd(Animator animator, Component clickView) {
        if (animator != null) {
            animator.stop();
        }
        if (handler == null) {
            initHandler();
        }
        handler.postTask(() -> {
            hide(clickView);
        }, builder.duration);
    }

    /**
     * 获取显示bottomSnackBar的界面的最上层ComponentParent 用于添加bottomSnackBar
     *
     * @param component 最上层ComponentParent
     */
    private void getRootView(Component component) {
        if (component != null && component.getComponentParent() instanceof ComponentContainer) {
            mRootView = (ComponentContainer) component.getComponentParent();
            getRootView(mRootView);
        }
    }

    /**
     * 初始化默认的SnackBar
     */
    private void initDefaultSnackBar() {
        defaultSnackBar = LayoutScatter.getInstance(getContext()).parse(ResourceTable.Layout_bottom_snackbar, this,
                false);
        if (defaultSnackBar == null) {
            return;
        }
        defaultSnackBar.setLayoutRefreshedListener(component -> BOTTOM_SNACKBAR_HEIGHT = component.getHeight());
        mTvSnackBar = (Text) defaultSnackBar.findComponentById(ResourceTable.Id_tv_snackbar);
        mTvSnackBar.setText(builder.snackBarContent);
        mTvSnackBar.setTextColor(builder.textColor);
        mTvSnackBar.setTextSize(builder.textSize, Text.TextSizeType.FP);
        ShapeElement element = new ShapeElement();
        element.setRgbColor(builder.backGroundColor);
        mTvSnackBar.setBackground(element);
    }

    /**
     * 控件显示之前获取它的宽高
     *
     * @param component 需要在显示之前获取宽高的控件
     * @return 控件的宽高
     */
    private int[] getWidthHeightBeforeShow(Component component) {
        int[] size = new int[]{0, 0};
        if (component == null) {
            return size;
        }
        int tempWidth = EstimateSpec.getSizeWithMode(0, EstimateSpec.UNCONSTRAINT);
        int tempHeight = EstimateSpec.getSizeWithMode(0, EstimateSpec.UNCONSTRAINT);
        component.estimateSize(tempWidth, tempHeight);
        size[0] = component.getEstimatedWidth();
        size[1] = component.getEstimatedHeight();
        return size;
    }

    /**
     * 建造者
     */
    public static class Builder {
        private long duration = DEFAULT_DURATION;
        private int animationDuration = ANIMATION_DEFAULT_DURATION;
        private RgbColor backGroundColor = new RgbColor(0, 0, 0, 120);
        private Color textColor = Color.RED;
        private int textSize = 16;
        private boolean isNeedShowBar = true;
        private String snackBarContent = "";
        private WeakReference<Context> mContext;
        private final Object lock = new Object();
        private BottomSnackBar snackBar;

        public Builder(Context context) {
            this.mContext = new WeakReference<>(context);
        }

        public Builder setDuration(long duration) {
            this.duration = duration;
            return this;
        }

        public long getDuration() {
            return duration;
        }

        public Builder setAnimationDuration(int animationDuration) {
            this.animationDuration = animationDuration;
            return this;
        }

        public Builder setBackGroundColor(RgbColor backGroundColor) {
            this.backGroundColor = backGroundColor;
            return this;
        }

        public Builder setTextColor(Color textColor) {
            this.textColor = textColor;
            return this;
        }

        public Builder setTextSize(int textSize) {
            this.textSize = textSize;
            return this;
        }

        public Builder setSnackBarContent(String snackBarContent) {
            this.snackBarContent = snackBarContent;
            return this;
        }

        public Builder setNeedShowBar(boolean needShowBar) {
            isNeedShowBar = needShowBar;
            return this;
        }

        /**
         * 构建BottomSnackBar
         *
         * @return BottomSnackBar对象
         */
        public BottomSnackBar build() {
            if (mContext == null || mContext.get() == null) {
                return new BottomSnackBar(null, this);
            }
            if (snackBar == null) {
                synchronized (lock) {
                    if (snackBar == null) {
                        snackBar = new BottomSnackBar(mContext.get(), this);
                        snackBar.initWidget();
                        return snackBar;
                    }
                }
            }
            return snackBar;
        }


    }
}
